﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace SemanticEx.Extensions
{
	public static class RegexExtensions
    {
        private static Dictionary<string, Regex> _regexDict;
        private static Dictionary<string, Regex> regexDict
        {
            get
            {
                if (_regexDict == null)
                {
                    _regexDict = new Dictionary<string, Regex>();
                }
                return _regexDict;
            }
            set
            {
                _regexDict = value;
            }
        }

        public static int DictionaryCount { get { return regexDict.Count; } }

#region "Private Support Methods"
        private static bool IsOddValueOnNegativeBitShift(int block, int order)
        {
            return (((block >> order) % 2) == 1) ? true : false;
        }

        private static int GetMaxBlocksize(int blockValue)
        {
            // 0x0000 - 0x000F = 0 - 15
            // 0x0010 - 0x00FF = 16 - 255
            // 0x0100 - 0x0FFF = 256 - 4095
            // 0x1000 - 0xFFFF = 4096 - 65635
            // ...
            if (!(blockValue > 0x0000000F))
            {
                return 0x000F;
            }
            else if (!(blockValue > 0x000000FF))
            {
                return 0x00FF;
            }
            else if (!(blockValue > 0x00000FFF))
            {
                return 0x0FFF;
            }
            else if (!(blockValue > 0x0000FFFF))
            {
                return 0xFFFF;
            }
            else if (!(blockValue > 0x000FFFFF))
            {
                return 0xFFFFF;
            }
            else if (!(blockValue > 0x00FFFFFF))
            {
                return 0xFFFFFF;
            }
            else if (!(blockValue > 0x0FFFFFFF))
            {
                return 0xFFFFFFF;
            }
            else
            {
                return int.MaxValue;
            }
        }

        private static void zeroMagnitudeArrays(ref int[] orderMin, ref int[] orderMax)
        {
            foreach (int item in orderMax)
	        {
                orderMax[item] = 0;
                orderMin[item] = 0;
	        }
        }

        private static bool IsValidPositiveRange(int from, int to)
        {
            if (to < from)
            {
                throw new Exception("The To value must be a larger positive value than the From value");
            }
            return true;
        }

        private static bool AreEqualRangeValues(int from, int to)
        {
            if (from == to)
            {
                return true;
            }
            return false;
        }
#endregion

        public static int VariableCount(this Regex @this)
        {
            return regexDict.Count;
        }

        public static Regex VariableCount(this Regex @this, out int count)
        {
            count = regexDict.Count;
            return @this;
        }

        public static Regex AddVariable(this Regex @this, string keyName)
        {
            if (!regexDict.ContainsKey(keyName))
            {
                regexDict.Add(keyName, @this);
            }
            return @this;
        }

        public static Regex AddVariable(this Regex @this, string keyName, string regex)
        {
            if (!regexDict.ContainsKey(keyName))
            {
                regexDict.Add(keyName, new Regex(regex));
            }
            return @this;
        }

        public static Regex RemoveVariable(this Regex @this, string keyName)
        {
            if (regexDict.ContainsKey(keyName))
            {
                regexDict.Remove(keyName);
            }
            return @this;
        }

        public static Regex GetVariable(this Regex @this, string keyName)
        {
            if (regexDict.ContainsKey(keyName))
            {
                return regexDict[keyName];
            }
            return @this;
        }

        public static bool VariableExists(this Regex @this, string keyName)
        {
            if (regexDict.ContainsKey(keyName))
            {
                return true;
            }
            return false;
        }

        public static Regex ClearVariables(this Regex @this)
        {
            regexDict.Clear();
            return @this;
        }

        public static Regex Range(this Regex @this, int from, int to)
        {
            if (!IsValidPositiveRange(from, to) ||
                AreEqualRangeValues(from, to))
            {
                return @this;
            }

            // Isolate true value for simpler calculation, restore later
            int fNegator = 1;
            int tNegator = 1;
            if (from < 0)
            {
                fNegator *= -1;
                from *= -1;
            }
            if (to < 0)
            {
                tNegator *= -1;
                to *= -1;
            }

            int maxSize = (int)Math.Log10(int.MaxValue) + 1;
            int[] orderOfMagnitudeMax = new int[maxSize];
            int[] orderOfMagnitudeMin = new int[maxSize];

            zeroMagnitudeArrays(ref orderOfMagnitudeMin, ref orderOfMagnitudeMax);


            // Note:  For loop logic leverages C#'s truncation of double->integer remainders
            for (int i = (int)Math.Log10(to); i >= 0; i--)
            {
                // Assume value of 280 for simplicity:
                //      A value > 255 carries the hex order, but not the decimal order
                double magnitudeFactor = Math.Pow(.1, i);
                orderOfMagnitudeMax[i] = (int)(to * magnitudeFactor) % 10;
            }

            for (int i = (int)Math.Log10(from); i >= 0; i--)
            {
                double magnitudeFactor = Math.Pow(.1, i);
                orderOfMagnitudeMin[i] = (int)(to * magnitudeFactor) % 10;
            }


            //// DEPRICATED :: USING HEXIDECIMAL LOLGIC
            //int block = 0;
            //int blocksize = GetMaxBlocksize(to);
            //// Iterate through the bits respect to the max hex order of magnitude
            //for (int i = ((int)Math.Log(blocksize + 1, 2) - 1); i > 0; i--)
            //{
            //    if (IsOddValueOnNegativeBitShift(to, i))
            //    {
            //        block = block >> 1;
            //    }
            //}


            // Perform Regex Expression Generation
            //   ?something like...    @this.Append("").GreaterThan();

            

            int segmentCount = 0;
            string rangeRegex = "(";
            for (int i = (int)Math.Log10(int.MaxValue); i >= 0; i--)
            {
                string defSet = string.Empty;
                if (orderOfMagnitudeMax[i] != 0)
                {
                    defSet += orderOfMagnitudeMax[i].ToString();
                    // Todo:  Make this recursive since we will never know the true depth
                    for (int n = i-1; n >= 0; n--)
                    {
                        if (orderOfMagnitudeMin[n] != orderOfMagnitudeMax[n])
                        {
                            defSet += "[" + orderOfMagnitudeMin[n] + "-" + orderOfMagnitudeMax[n] + "]";
                        }
                        else
                        {
                            defSet += orderOfMagnitudeMax[n];
                        }
                    }
                    segmentCount++;
                    rangeRegex += defSet;
                }
                if (segmentCount > 1)
                {
                    rangeRegex += "|";
                }
            }
            rangeRegex += ")";

            //rangeRegex = GenerateMaxValueExpression(0);
            rangeRegex = GenerateMaxValueExpression(9);
            rangeRegex = GenerateMaxValueExpression(10);
            rangeRegex = GenerateMaxValueExpression(to);

            return new Regex(rangeRegex);
        }

        // BUG:  Boo, hiss, you've got an infinite loop!!!
        private static string GenerateMaxValueExpression(int max)
        {
            string maxExp = string.Empty;

            for (int i = (int)Math.Log10(max); i >= 0; i--)
            {
                // We've reached the end of the recursive loop, time to generate the expression
                if (i == 0)
                {
                    maxExp += GenerateSegment(0, max);
                    continue;
                }

                // Let's isolate the value of the highest order of magnitude
                int magnitudeValue = 0;
                double magnitudeFactor = Math.Pow(.1, i);

                // Perform a recursive drill down
                if (i == 1)
                {
                    // TODO:  This algorigm crashes for an exact 10 match, fix.
                    magnitudeValue = ((int)(max * magnitudeFactor) % 10) * ((int)Math.Pow(10, i));
                    maxExp += GenerateMaxValueExpression(max - magnitudeValue);
                }
                else
                {
                    magnitudeValue = ((int)(max * magnitudeFactor) % 10) * ((int)Math.Pow(10, i));
                    maxExp += GenerateMaxValueExpression(max - magnitudeValue);
                }
            }

            return maxExp;
        }

        private static string GenerateSegment(int min, int max)
        {
            if (max > min)
            {
                if (max == 10)
                {
                    return "1[0-9]";
                }
                return string.Format("[{0}-{1}]", min, max);
            }
            return max.ToString();
        }

        private static Regex GreaterThan(this Regex @this, string min)
        {
            // TODO:  Algorithm to Perform Regex Expression Generation
            return @this;
        }

        private static Regex LessThan(this Regex @this, string min)
        {
            // TODO:  Algorithm to Perform Regex Expression Generation
            return @this;
        }

        public static Regex Filter(this Regex @this, string subEx)
        {
            return new Regex(subEx);
        }

        public static Regex Append(this Regex @this, string appendEx)
        {
            return new Regex(@this.ToString() + appendEx);
        }

		public static IEnumerable<string> GetValues(this GroupCollection @this)
		{
			return @this.Cast<Group>().Skip(1)
				.Aggregate(Enumerable.Empty<string>(),
					(list, grp) => list.Concat(grp.Captures.Cast<Capture>().Select(x => x.Value)));
		}

		public static IEnumerable<string> GetValues(this Validation @this)
		{
			return @this.Match.Groups.GetValues();
		}

		public static Validator Validate(this Validator @this, string groupName, Func<string, bool> validation)
		{
			var validator = new Validator(@this);
			validator.AddValidation(groupName, validation);
			return validator;
		}

		public static Validator Validate(this Regex @this, string groupName, Func<string, bool> validation)
		{
			return new Validator(@this).Validate(groupName, validation);
		}

		public static Validation Validate(this Match @this, string groupName, Func<string, bool> validation)
		{
			var group = @this.Groups[groupName];
			var isValid = @this.Success;
			try
			{
				foreach (Capture capture in group.Captures)
				{
					isValid &= validation(capture.Value);
				}
			}
			catch (Exception ex)
			{
				isValid = false;
				throw ex;
			}
			return new Validation(@this, isValid);
		}

		/// <summary>
		/// Accepts a validation expression of the type "{groupName}=>{boolean predicate}".
		/// {groupName} is the only variable allowed in the boolean expression.
		/// </summary>
		public static Validator Validate(this Regex @this, string validationExpression)
		{
			var info = RegexValidationParser.Parse(@this, validationExpression);
			return @this.Validate(info.GroupName, info.Validation);
		}

		/// <summary>
		/// Accepts a validation expression of the type "{groupName}=>{boolean predicate}".
		/// {groupName} is the only variable allowed in the boolean expression.
		/// </summary>
		public static Validator Validate(this Validator @this, string validationExpression)
		{
			var info = RegexValidationParser.Parse(@this.Regex, validationExpression);
			return @this.Validate(info.GroupName, info.Validation);
		}
	}
}